package algo_lignes_sites.data.junction;

import algo_lignes_sites.data.path.A_Path;
import algo_lignes_sites.data.path.PathREF;
import algo_lignes_sites.data.pre_trt.pre_trt_path.SegmentTopo;
import algo_lignes_sites.params.objects.contraintesgeom.ContrainteGeomNode;
import commons.data.Attributes;
import commons.data.EchelleGeneralization;
import commons.data.Zone;
import commons.data.Zones;
import commons.io.in.Point_in;
import commons.utils.UtilsCalcul;
import commons.utils.primitive_geom.Point_d;
import gnu.trove.set.hash.THashSet;
import java.awt.Color;
import java.util.ArrayList;
import java.util.HashSet;

public class JunctionREF extends A_Junction {


	// Lien vers le point DER equivalent : ce lien est établi en fin de pretraitement
	//  lors de la création des données DER
	JunctionDER jDER;
	public JunctionDER getJunctionDER() {return jDER;}

	
	//*********************************************************************************
	// Données utilisées pendant la phase de création de la topologie (création des path)
	public int nbSegConnected;
	public THashSet<SegmentTopo> setSegConnected;
	public void addSegment(SegmentTopo seg) {
		if (setSegConnected==null)
			setSegConnected = new THashSet<SegmentTopo>();
		if (setSegConnected.add(seg))
			nbSegConnected++;
	}
	// Attention, utilisé uniquement pour accélérer le traitement de construction des Path
	// notamment, on ne change pas la variable nbSegConnected qui elle est laissée constante
	//  durant le traitement
	public void removeSeg(SegmentTopo seg) {
		assert setSegConnected.contains(seg);
		setSegConnected.remove(seg);
	}

	
	//*********************************************************************************
	// Complexite de la zone dans laquelle se trouve la junction
	public double complexiteZone=Double.NEGATIVE_INFINITY;
	// selon les paramètres utilisateur, cette complexité peut se traduire en une atténuation de mouvement
	public double attenuationMouvement_zoneDense=1;
	
	
	//*********************************************************************************
	// Utilisé lorsqu'il y a un ménage à faire dans le vecteur global de JunctionREF
	//  notamment après la simplification des node ou des tracé de path
	public boolean bToBeKept=false;
	
	// Nettoyage du vecteur global vJunction : on ne garde que celle qui sont référencées
	//  au moins une fois dans un des path, et au moins tous les Nodes (cf. il peut y avoir des nodes isolés)
	static public void cleanVJunctionREFGlobal(ArrayList<PathREF> vAllPathREF, ArrayList<JunctionREF> vAllJunctionREF) {
		// Reinitialisation du flag de suppression
		for (JunctionREF jREF : vAllJunctionREF)
			jREF.bToBeKept = false;
		
		// Puis on va parcourir les path, et flager leur junction comme étant à conserver dans le vecteur global
		int nbJunctionToKeep = 0;
		for (PathREF p : vAllPathREF)
			for (JunctionREF j : p.getVJunctionREF()) {
				j.bToBeKept = true;
				nbJunctionToKeep++;
			}
		// -> puis on n'ajoute pas les junctions flagées dans le nouveau vecteur de junction global
		ArrayList<JunctionREF> new_vJunctionREF = new ArrayList<JunctionREF>(nbJunctionToKeep+100); // 100 = possibles node isolés dans les données (au pif)
		for (JunctionREF jREF : vAllJunctionREF) {
			if (jREF.bToBeKept || jREF.isNode())
				new_vJunctionREF.add(jREF);
		}
		vAllJunctionREF.clear();
		vAllJunctionREF.addAll(new_vJunctionREF);
	}

	
	//*********************************************************************************
	// Donnée permettant de savoir si la junction est issue d'un découpage Seg/Zone
	//  permettant par la suite de snapper la junctionDER correspondante sur le bord de la zone
	// = null si junction "normale"
	public Zone zoneIfIntersectSegZone;


	public JunctionREF (double x, double y, TypeJunction typePoint) {
		super(x, y, typePoint);
	}
	public JunctionREF (double x, double y, TypeJunction typePoint, PathREF path) {
		super(x, y, typePoint, path);
	}
	public JunctionREF (Point_d pt, TypeJunction typePoint) {
		this(pt.x, pt.y, typePoint);
	}
	public JunctionREF (Point_d pt, TypeJunction typePoint, PathREF path) {
		super(pt, typePoint, path);
	}
	// Pour créer une junction type NODE
	// vNodeInitFusionne : algo >= v22 : si ce node est temporaire car fusion d'autres node durant le traitement
	public JunctionREF (double x, double y, Attributes attributes, Zones zones, ContrainteGeomNode cgn, EchelleGeneralization eg, Color color, ArrayList<Point_in> vNodeInitFusionne) {
		super (x, y, attributes, zones, cgn, eg, color, vNodeInitFusionne);
	}
	// vNodeInitFusionne : algo >= v22 : si ce node est temporaire car fusion d'autres node durant le traitement
	public JunctionREF (Point_d pt, Attributes attributes, Zones zones, ContrainteGeomNode cgn, EchelleGeneralization eg, Color color, ArrayList<Point_in> vNodeInitFusionne) {
		this (pt.x, pt.y, attributes, zones, cgn, eg, color, vNodeInitFusionne);
	}

	@Override
	protected double distTo_ref(A_Junction j) {
		return this.distTo((JunctionREF)j);
	}
	
	@Override
	String getInfoDetaille_specific() {
		String res = "";
		if (isNode()) {
			res += "Information de simplification : \n";
			res += " - facteur correctif = " + UtilsCalcul.round2(nodeData.k_correctif) + "\n";
		}
		res += "Complexité de la zone    = " + UtilsCalcul.round2(complexiteZone) + "\n";
		res += "Atténuation de mouvement = " + UtilsCalcul.round2(attenuationMouvement_zoneDense) + "\n\n";

		if (jDER!=null) {// jDER peut etre null si cette fonction est appelée pour un assert dans une phase de pretraitement ...
			res += "Nb node entraineur        : " + jDER.getNbInfluenceur_NODE() + "\n";
			for (int i=0; i<jDER.getNbInfluenceur_NODE() ; i++)
				res += " - " + jDER.getInfluenceur_NODE(i) + " : " + UtilsCalcul.round2(jDER.getFactorInfluencement_NODE(i)) + "\n";


			res += "Nb pt multiple entraineur : " + jDER.getNbInfluenceur_PTMULTIPLE() + "\n";
			for (int i=0; i<jDER.getNbInfluenceur_PTMULTIPLE() ; i++)
				res += " - " + jDER.getInfluenceur_PTMULTIPLE(i) + " : " + UtilsCalcul.round2(jDER.getFactorInfluencement_PTMULTIPLE(i)) + "\n";
		}
		return res;
	}
	
	
	//**************************************************************************
	//       TOPOLOGIE propre aux nodes
	//**************************************************************************

	// Calcul de la notion de connexion directe/indirecte path->node
	// C'est une notion locale (on ne va pas plus loin qu'un facteur de la largeur du node
	// On le calcule des le départ, donc avec les dimensions finales des nodes et des path (échelle finale)
	
	// Calculé une unique fois durant le pretraitement sur les Node REF
	// Cela sert notamment à la phase de simplification des nodes, et pour sous-decouper les path connectés à ce node
	//  à des endroits proches pour adoucir les mouvements d'entrainement

	// Pour les Node DER (qui récupèrent cette info lors de leur création dans le Data_ls,
	//  la connexité des path sur ce node, va permettre de calculer
	//  les atténuations des forces de répulsion et les forces d'entrainement des points
	//  de ces path par rapport a ce node
	
	public void computeTopoConnexion() {
		assert(this.isNode()) : "computeTopoConnexion() sur type : " + this.typeJunction;
		assert (this instanceof JunctionREF) : "computeTopoConnexion() sur JunctionDER";
		
		double rayon_final;
		// rayon de connexité = distance max de parcours des Path
		// On va jusqu'à l'influence nulle du node
		double rayon_connexite;

		rayon_final = getRayonNodeFinal();
		rayon_connexite = rayon_final * nodeData.contrainteGeom.zc;


		// Set pour éviter de repasser plusieurs fois sur le meme path
		// (cf. appel récursif + cas des path qui reviennent sur les mêmes extrémités)
		HashSet<A_Path> setPathDejaTraites = new HashSet<A_Path>();
		for (int i=0; i<nbPathContenant; i++) {
			A_Path path = getPathContenantNum(i);
			traiteConnexionPath_toThisNODE_rec(path, this, rayon_connexite, setPathDejaTraites);
		}
	}


	
	// Déclare path comme connecté à this, et lance le traitement sur les autres
	//  path relié à path pour déterminer en plus les connexités INDIRECTES
	// Cas spécial des intersection : on poursuit sur les path reliés à cette
	//  intersection uniquement si cette intersection leur est extrémité
	// si le path traverse l'intersection, on ne fait rien, on considère qu'il
	//  n'est pas connecté à l'intersection
	private void traiteConnexionPath_toThisNODE_rec(A_Path path, A_Junction jDepart, double distMax, HashSet<A_Path> setPathDejaTraites) {
		if (setPathDejaTraites.contains(path))
			return; // Path déjà traité

		// On indique le pathDER comme déjà traités pour ne pas y revenir par la suite
		setPathDejaTraites.add(path);

		// On déclare le path connecté au node
		path.addNodeConnected(this);

		// Puis on propage le traitement aux path connectés à path
		// On cherche la junction jSuite (autre que jDepart) pour poursuivre le parcours
		A_Junction j1 = path.from();
		A_Junction j2 = path.to();
		assert (j1==jDepart || j2==jDepart) : "Aucune des junctions extrémités du path ne correspondent à jDepart node = " + this + " path = " + path.getInfoBulle() + "\n" +
				j1.getInfoDetaillee() + "\n\n" + j2.getInfoDetaillee() + "\n\n index 1 = " + path.getIndexOfThisJunction(j1) + "\nindex2 = " + path.getIndexOfThisJunction(j2);
//		if (j1!=jDepart && j2!=jDepart)
//			j1=j1;

		A_Junction jSuite;
		if (j1!=jDepart)
			jSuite = j1;
		else if (j2!=jDepart)
			jSuite = j2;
		else
			// Le Path revient sur lui-même, au  niveau de jDepart, il n'y a rien à poursuivre
			jSuite = null;

		if (jSuite != null) {
			// A-t-on atteint la dist max de recherche ?
			// Rappel : la distance est basée sur les coordonnées REF d'origine !
			if (this.distTo_ref(jSuite) <= distMax) {

				switch (jSuite.typeJunction) {
					case NODE:
						break;
					case INTERSECT:
						// parcours des path connectés à jSuite et appel récursif du traitement
						//  uniquement si jSuite est une extrémité du path
						for (int i=0; i<jSuite.nbPathContenant; i++) {
							A_Path path_2 = jSuite.getPathContenantNum(i);
							if (jSuite.isExtremite(path_2))
								traiteConnexionPath_toThisNODE_rec (path_2, jSuite, distMax, setPathDejaTraites);
						}
						break;
					case STD:
					case CONNECT_2:
					case CONNECT_3:
						// parcours des path connectés à jSuite et appel récursif du traitement
						for (int i=0; i<jSuite.nbPathContenant; i++) {
							A_Path path_2 = (A_Path) jSuite.getPathContenantNum(i);
							traiteConnexionPath_toThisNODE_rec (path_2, jSuite, distMax, setPathDejaTraites);
						}
						break;
					default:
						assert(false) : "Type de junction inconnu : " + jSuite.typeJunction;
				}
			}
		}

		// Enfin, on parcourt les autres junction du path (pas les extrémités) à la recherche d'intersection
		//  c'est en effet le seul cas où il peut y avoir un path connecté à un autre
		//  sans que ce soit au niveau d'une des 2 extrémités,
		//  (contrairement au CONNECT_2et3 qui sont nécessairement extrémité de tous les path connectés)
		ArrayList<A_Junction> vJunction = path.getVJunction();
		int nbIter = vJunction.size()-1;
		for (int i=1; i<nbIter; i++) {
			A_Junction intersect = vJunction.get(i);
			if (!intersect.isIntersect()) continue;
			if (this.distTo_ref(intersect) > distMax) continue;// Rappel : la distance est basée sur les coordonnées REF d'origine !
			// parcours des path connectés à intersect et appel récursif du traitement uniquement si l'intersect est extrémité du path
			for (int j=0; j<intersect.nbPathContenant; j++) {
				A_Path path_2 = (A_Path) intersect.getPathContenantNum(j);
				if (intersect.isExtremite(path_2))
					traiteConnexionPath_toThisNODE_rec (path_2, intersect, distMax, setPathDejaTraites);
			}
		}
	}



}
